package utils

import (
	"os"
	"reflect"
	"strings"
	"time"
)

func Struct2Map(structName interface{}) map[string]interface{} {
	t := reflect.TypeOf(structName)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	if t.Kind() != reflect.Struct {
		panic("param is not a struct")
	}
	v := reflect.ValueOf(structName)
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}
	fieldNum := t.NumField()
	result := make(map[string]interface{}, fieldNum)
	for i := 0; i < fieldNum; i++ {
		kind := t.Field(i).Type.Kind()
		if kind == reflect.Struct || kind == reflect.Interface || kind == reflect.Func || kind == reflect.Chan {
			continue
		}
		field := t.Field(i).Name
		if field == "Id" {
			continue
		}
		value := v.Field(i).Interface()
		switch kind {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
			reflect.Uint, reflect.Uint32, reflect.Float32, reflect.Float64:
			if value == 0 {
				continue
			}
		case reflect.String:
			if value == "" {
				continue
			}
		}
		result[field] = value
	}
	return result
}

func CopyProperties(source interface{}, dest interface{}) {
	if reflect.ValueOf(source).Kind() != reflect.Ptr || reflect.ValueOf(dest).Kind() != reflect.Ptr {
		panic("source and dest param must be ptr")
	}
	aValue := reflect.ValueOf(source)
	bValue := reflect.ValueOf(dest)
	aType := aValue.Type().Elem()
	bType := bValue.Type().Elem()
	if aType.Kind() != reflect.Struct {
		panic("source must be typeof struct")
	}
	if bType.Kind() != reflect.Struct {
		panic("source must be typeof struct")
	}
	for i := 0; i < aType.NumField(); i++ {
		if f, ok := bType.FieldByName(aType.Field(i).Name); ok && aType.Field(i).Type == f.Type {
			bValue.Elem().FieldByName(aType.Field(i).Name).Set(reflect.ValueOf(aValue.Elem().Field(i).Interface()))
		}
	}
}

func CopyNotEmptyProperties(source interface{}, dest interface{}) {
	if reflect.ValueOf(source).Kind() != reflect.Ptr || reflect.ValueOf(dest).Kind() != reflect.Ptr {
		panic("source and dest param must be ptr")
	}
	aValue := reflect.ValueOf(source)
	bValue := reflect.ValueOf(dest)
	aType := aValue.Type().Elem()
	bType := bValue.Type().Elem()
	if aType.Kind() != reflect.Struct {
		panic("source must be typeof struct")
	}
	if bType.Kind() != reflect.Struct {
		panic("source must be typeof struct")
	}
	for i := 0; i < aType.NumField(); i++ {
		aFieldValue := aValue.Elem().Field(i)
		if f, ok := bType.FieldByName(aType.Field(i).Name); ok && aFieldValue.Type().Kind() == f.Type.Kind() {
			if aFieldValue.Interface() == nil {
				continue
			}
			switch num := aFieldValue.Interface().(type) {
			//case int, int8, float64, float32:
			//	if num == 0 {
			//		continue
			//	}
			//	break
			case string:
				if num == "" {
					continue
				}
			case time.Time:
				if num.IsZero() {
					continue
				}
			}
			bValue.Elem().FieldByName(aType.Field(i).Name).Set(aFieldValue)
		}
	}
}


func GetPkgRealPath(pkgPath string) string {
	pathParts := strings.Split(pkgPath, "/")
	wd, _ := os.Getwd()
	wParts := strings.Split(wd, "/")
	idx := 0
	wPartsLen := len(wParts)
	pathPartsLen := len(pathParts)
	for i := wPartsLen - 1; i >= 0; i-- {
		if idx < pathPartsLen && pathParts[idx] == wParts[i] {
			idx++
		} else {
			break
		}
	}
	if idx > 0 {
		return strings.Join(wParts[0:(wPartsLen - idx)], "/") + "/" + strings.Join(pathParts[idx-1:], "/")
	}
	return  wd + pkgPath
}

